Skip to content

Merge train: #9861, #9965, #9969, #9970, #9971, #9973, #9975 - #9978

Merged
proggeramlug merged 20 commits into
mainfrom
train143
Sep 7, 2026
Merged

Merge train: #9861, #9965, #9969, #9970, #9971, #9973, #9975#9978
proggeramlug merged 20 commits into
mainfrom
train143

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Merge train: #9861, #9965, #9969, #9970, #9971, #9973, #9975.

The 13 open PRs split into two trains because six of them form two stacks (#9918#9958#9977 regex, #9950#9957#9976 GC); those follow separately so a failure in ~8k lines of GC and regex work stays attributable.

#9969 makes the local lint driver honest — and it immediately found things

run_lint_gates.sh derives its gate list from test.yml, and its extractor only accepted commands starting with python3 scripts/, ./scripts/ or cargo fmt. It derived 67 commands from 44 run: steps, ran 65, and printed green. Verified against pristine origin/main before accepting the claim: the driver never referenced the Public benchmark evidence freshness step, that step is in the lint job, and it fails on main — red since 2026-07-29.

A second blind spot in the same driver: the compile tier ran only cargo check --workspace --all-targets, where test targets keep dead code alive, and never the product-scope -D warnings check. That fails on main with seven dead-code errors, which is exactly #9970.

After the fix the driver derives 80 gates, and two of the newly-run ones were red on this train:

#9975 retires a pin correctly

#7254's arm asserted its reproducer still panics. With the bug fixed that pin must invert, and the replacement is stronger than a bare pass: Node runs as oracle and the arm fails loudly if the oracle itself cannot run the workload, output must be byte-identical to Node's, and copied_objects > 0 — so the arm fails if the copying minor never ran, rather than passing vacuously.

Remaining gate state

run_lint_gates: 3 of 80 FAILED on this train, all accounted for:

  • public-baseline — pre-existing on main, verified on a pristine worktree. Regenerating that artifact is ~2h on the bench mini and tooling(lint): run_lint_gates.sh fails loudly when a lint step yields no command — it silently dropped the public-baseline step #9969 explicitly scopes it out as separate maintenance.
  • regen API docs and API docs drift — an artifact of the validation harness, not the code. regen_api_docs.sh hardcodes $ROOT/target/release/perry; this session builds with CARGO_TARGET_DIR pointed elsewhere. With the binary placed where the script expects, regen_api_docs.sh succeeds and the drift check is clean — the committed docs match what this tree generates.

Validation

suite passed failed
perry-runtime 3275 0
perry-codegen 1943 0
perry-hir 630 0
perry-stdlib 132 0

Summary by CodeRabbit

  • Bug Fixes

    • Improved garbage-collection evacuation verification to prevent diagnostic-path crashes and provide more actionable failure details.
    • Refined adaptive tenuring decisions for more accurate survivor tracking and safer promotion behavior.
    • Corrected native integration result handling across networking, databases, media, UI, and other supported modules.
  • Diagnostics

    • Added optional runtime diagnostics for receiver representations and evacuation verification.
  • Builds

    • Segment-view configuration changes now correctly invalidate affected build caches.

proggeramlug and others added 20 commits September 7, 2026 22:58
Keep the no-default-features product build free of dead-code warnings while
retaining every helper where its regex feature or unit tests need it.

Dispositions:
- newborn_parent_needs_barrier: cfg(test or regex-engine); feature + tests use it.
- REGEXP_PROTOTYPE_PTR_SLOT: cfg(test or regex-engine); feature GC root backing.
- REGEXP_PROTOTYPE_TEST_CLOSURE_SLOT: cfg(test or regex-engine); feature root backing.
- REGEXP_PROTOTYPE_TEST_INDEX_SLOT: cfg(test or regex-engine); feature scalar backing.
- REGEXP_PROTOTYPE_PTR: cfg(test or regex-engine); feature fast path/root scanner.
- REGEXP_PROTOTYPE_TEST_CLOSURE: cfg(test or regex-engine); feature fast path/scanner.
- REGEXP_PROTOTYPE_TEST_WALKS: cfg(test or regex-engine); feature/test diagnostic.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit 31a9d08)
The extractor only admitted commands beginning with `python3 scripts/`,
`./scripts/`, or `cargo fmt`. The public-baseline commands instead begin
with an inline PYTHONPATH assignment and `python3 benchmarks/`, so both
were filtered out; the YAML block's comments and step metadata were not
the cause.

Recognize every executable family currently used by lint, retain workflow
step names in the derived list, and reject any run step that produces no
commands. Keep the two GitHub-context commands on a stale-checked explicit
skip list, and self-test both comment-led extraction and the loud failure.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit 75885c0)
Derive the warnings and check job gates from test.yml, including the
product-only warnings check, both host-compatible workspace scopes, and the
API-docs regeneration and drift assertion. Expand the workflow's package
exclusions portably for macOS Bash 3.2.

Extend the extractor self-test to require the product check under -D warnings,
reject its removal, and prove newly added warnings commands are replayed.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit 3e6aeef)
Replace arm 5b's expected verifier panic with positive correctness and
liveness checks for the retained-growth workload. Compare Perry's stdout
byte-for-byte with Node, require a successful exit, and require copied
objects so the evacuation verifier cannot pass vacuously.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit 83b90a0)
Include PERRY_SEGVIEW in the build-cache environment fingerprint so
switching the lowering cannot reuse a binary built under the other mode.
Record PERRY_SEGVIEW_DIAG as diagnostics-only, and cover the real cache
miss with a qualifying Intl.Segmenter compile.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit 9929ed0)
…opy (#9851)

The adaptive tenuring loop takes its one and only survivor-round mortality
sample on the FIRST minor of the process -- when the cohort really is immortal
(99.1 % survival) -- drops the threshold to 1, and thereby destroys its ability
to ever sample again: n=1 across 352 minors. In steady state an aging round
filters 26.1 % of each cohort, and the loop cannot see it.

`retune_after_scavenge` picks the threshold from `S = 1 + desired / influx`, the
largest S whose projected survivor occupancy `(S-1) x influx` fits the desired
survivor size. With integer division, any influx above `desired` yields exactly
1 -- there is no rung at 2 or 3. On the compiled claude-code TUI the first drop
reads `eden_live_bytes=12075344` against `desired=1048576`.

S=1 does not reduce the surviving data; it relocates it, from the survivor space
-- where the next minor re-examines it for free -- to the old generation, which
only a full can reclaim. The occupancy formula has no term for that. And S=1 is
self-sealing: nothing is copied, so `copied_bytes` is 0, so next cycle
`prev_copied` is 0, so the survival-rate lock's guard (`prev_copied >=
substantial`) is false forever. Both remaining exits -- the occupancy recompute
and PROMOTE_LOCK's unlock -- are QUIET-INFLUX exits, which say nothing about
lifetime. The loop concludes "long-lived" from a premise about space and then
removes its ability to check.

Measured, 4 streamed turns in one process, both arms from one binary via the
diagnostic knob PERRY_GC_TENURING_SURVIVALS, 3300-character replies:

                                   adaptive        pinned S=2
  minors at S=1            351 of 352 (100 % of promotion)     0
  threshold transitions                   1                    7
  mortality samples                       1                  393
  median mortality                     0.9 %               26.1 %
  ...steady turns 2 / 3 / 4    not measurable   26.1 / 26.1 / 26.1 %
  substantial cohorts < 10 %            1/1                5/358
  promoted                          1057 MB               792 MB

The occupancy rule now stops at the lowest threshold that still PRODUCES that
measurement. 2 is forced by the requirement rather than tuned: at S=1 nothing
enters the survivor space, at S=2 exactly one cohort does.

The clamp is at the USE SITE, not inside `compute_target_survivals`: that pure
function has a second caller, `full_seed_promotes_on_first_copy`, which gates the
sweep seed on `... != 1`. Clamping the shared function would silently disarm the
sweep seed, which is one of the two paths that IS allowed to reach 1. Reaching 1
still belongs to the survival-rate lock and the sweep seed, which measure
mortality; both are untouched, so the rule is self-limiting -- on a workload
whose cohort genuinely does not die the lock fires after one cohort's copy and
takes the loop back to 1. On claude-code it correctly does not: 5 of 358
substantial cohorts sit under the lock's 90 % bar, so the clamp holds rather
than oscillating.

Tests. `target_formula_matches_projected_occupancy` is byte-identical -- the
arithmetic is untouched, and that test is the proof. Four tests move an expected
value 1 -> 2 and keep their names, structure and invariants:
`drops_immediately_and_rises_debounced` (asymmetric response: immediate drop,
debounced rise -- 4 -> 2 shows it as well as 4 -> 1),
`steady_heavy_influx_is_a_fixed_point` (fixed-pointness, now at 2),
`heavy_influx_lowers_threshold_and_promotes_next_cycle` (its promotion half is
untouched: the cohort was copied once, so `next_age` is 2 on cycle 2 and it
still tenures exactly when the test says) and
`quiet_cycles_restore_power_on_threshold_debounced` (the debounced restore is
asserted structurally and survives). Two new tests: the two-phase attributed
pair -- occupancy alone holds at the floor and has not taken the lock's route,
then a substantial fully-surviving cohort still reaches 1 through the lock --
and a dying-cohort test at claude-code's measured 74 % survival.

(cherry picked from commit 925ceb2)
… survivor space

Follow-up to the previous commit, and caused by it. #9851's clamp stops the
occupancy rule concluding "promote on first copy", and measuring the relinked
candidate showed it buys -7 % of promotion where the pinned control buys -26 %:
85 % of promotion still happens at S=1, now reached through the survival-rate
lock 8-12 times per four-turn run.

That is a consequence of the clamp, not a coincidence. At S=1 nothing is copied,
so `prev_copied` is 0 and the lock's guard can never be satisfied -- the previous
commit's own argument. Removing the seal hands the lock its guard back, and the
lock then reaches 1 by itself.

The lock tested

    prev_copied >= substantial && survivor_live_bytes * 10 >= prev_copied * 9

where `survivor_live_bytes` is every live byte leaving the from-survivor space
this cycle, of any age, and `prev_copied` is the previous cycle's whole intake.
Those two scopes MATCH: the survivor spaces are a strict semispace pair (to-space
reset before the minor, everything copied into it, then flip), so the from-space
at cycle N holds exactly what cycle N-1 copied. The ratio is well-formed and
cannot exceed 1. The defect is not the arithmetic.

The defect is which POPULATION the ratio rates, and that is chosen by the very
threshold the lock sets. At S <= 2 the space holds one fresh cohort (age-2 is
promoted) and the ratio is one aging round's survival -- 74 % on the compiled
claude-code TUI, under the 90 % bar. At S = 3-4 it also holds age-2 and age-3
objects, which have already survived a round and are therefore selected for
longevity, so the aggregate clears 90 % while a fresh cohort does not. The rule
reads its own setting back as evidence. The clamp is what lets the debounced
rise reach 3 and 4, which is why this only became visible once the seal was gone.

The copier now accounts the fresh half of each cycle. `eden_copied_bytes` is
what this cycle copied out of EDEN into the to-survivor space (no re-copies) --
one cohort's intake. `survivor_first_round_live_bytes` is what came back out of
the from-survivor space alive with a stored survival age of 1, i.e. members of
exactly the cohort the previous cycle's `eden_copied_bytes` counted; the age is
already in the header at copy time (`copied_survival_age`), so no new per-object
state is needed. `retune_after_scavenge` keeps its arity and its two lock
parameters are redefined to those, which is the whole change at the policy end:
both sides of the ratio are now scoped to one cohort at every threshold.

Both new counts are on the `[gc-copy-minor]` diagnostic line next to the
whole-space ones, so first-round mortality is readable from ANY build rather
than only from an instrumented branch -- the measurement this policy is about
should not require a custom binary.

Measured, one binary, three arms via `PERRY_GC_TENURING_SURVIVALS`, 3300-char
replies, 4 turns in one process, macOS arm64:

  arm                          minors   promoted   S=1 share   via the lock
  =1 (pre-clamp equivalent)      356     1055 MB      100 %          -
  clamp only, run 1              368      982 MB       85 %          8
  clamp only, run 2              384      980 MB       84 %         12
  =2 (positive control)          380      785 MB        0 %         n/a

Tests. No existing expected value moves -- all 1,069 gc tests pass unchanged,
which is itself the finding: nothing in the suite distinguished the two scopes,
because they are equal on every heap whose survivor space holds one generation,
and that is every heap at a threshold of 2 or below. So the premise gets a test
of its own on a real heap: two rooted objects introduced one cycle apart at the
power-on threshold, asserting that the two numbers AGREE while only one
generation is resident and then DIFFER once an aged resident joins it, with the
aged object in the whole-space number and not in the cohort number. A test-only
witness (`test_last_cohort_split`) reports the pair the copier computed.

The two lock tests keep their values and gain the scoping in their names and
comments; `a_cohort_that_dies_in_its_round_holds_at_the_occupancy_floor` now
states that this same heap locks if the call site passes the whole space, which
is what it used to pass.

The previous commit's changelog fragment claimed the lock correctly stays out on
claude-code (5 of 358 substantial cohorts under the bar). That figure was taken
with the threshold PINNED, where every cohort the lock can rate is a first-round
cohort; it does not describe the rule running, and the fragment is corrected
rather than left to be read as a result.

Claude-Session: https://claude.ai/code/session_014knX724SYDogwzsXybCGxp
(cherry picked from commit c154ba6)
…d is measured

The symmetric half of #9851. That commit stopped the occupancy rule concluding
"promote on first copy" -- a claim about LIFETIME derived from a measurement of
SPACE. The same formula makes the same category error at the other end:

    compute_target_survivals = 1 + desired / influx      (capped at the ceiling)

returns the ceiling for a tiny influx AND for a zero one. On the first minors of
a process -- heap nearly empty, no cohort ever followed -- occupancy therefore
claims the MAXIMUM, before a single object has been given the chance to die.
It is the expensive direction of the error, because every survivor is then
copied up to three times before it may be promoted.

Measured on the landing base (main5 + #9881, one binary, four env arms, two
rounds of 4 turns at 3300 and 400, quiet host), this startup excursion is the
WHOLE difference between the adaptive loop and a pinned threshold:

  * unset vs pinned S=1: turn-1 CPU 3.41 s vs 3.02 s at 3300 (+0.35..0.45 s
    both rounds) and 1.05 s vs 0.72 s at 400 (+50 %), while the sum over turns
    2-4 is within noise (6.18-6.23 vs 6.35-6.41);
  * the adaptive arm's transitions are `4 -> 2 (occupancy) -> 1 (lock)` and ALL
    of them land inside turn 1; turns 2-4 run at S=1 with nothing copied.

So the adaptive policy's only cost on this workload was a startup claim it had
no evidence for, and its steady state was already the pinned one.

The rule is now symmetric: **until one survivor round has actually been rated,
the occupancy rule holds at `OCCUPANCY_MIN_SURVIVALS`.** That value is not a
tuning choice; it is the lowest threshold that PRODUCES the measurement the
rule needs in order to say anything -- at 1 nothing enters the survivor space,
at 2 exactly one cohort does. The power-on threshold becomes the same value for
the same reason: starting at the ceiling is a lifetime claim made before the
process has run.

`SURVIVOR_ROUND_MEASURED` is set the moment a cohort the previous cycle copied
becomes rateable, so the gate lifts after about two minors and the ladder is
unchanged from then on -- it delays the claim until evidence exists, it does not
remove the ladder. The two paths that MEASURE mortality are untouched: the
survival-rate lock and the sweep seed may still reach 1 whenever they have the
evidence for it.

`compute_target_survivals` is again left alone, and its test is again the proof:
the arithmetic still returns the ceiling for a zero and a tiny influx. Only what
the loop may do with that changes.

Tests. A new two-phase test: eight startup-shaped minors (tiny influx, nothing
copied) must leave the loop at the floor and out of the lock; then, once a
cohort has gone through the survivor space and been followed, the debounced rise
must still reach the ceiling. Sabotage: delete the gate, or restore the power-on
value to the ceiling, and phase 1 fails.

Two existing tests move with the power-on value and keep their properties:
`drops_immediately_and_rises_debounced` is about the ladder's ASYMMETRY, so it
now seeds a fully-dying cohort first (which rates a round without involving the
lock) and then tests the same immediate-drop / debounced-rise behaviour;
`sweep_seed_refuses_a_small_fully_live_eden` asserts the threshold is unchanged
from power-on, which is the floor now. `survival_rate_lock_breaks_a_saturated_
pipeline` needs no change -- the lock firing implies a rated round, so its
ladder recovery is unaffected.

NOT COMPILED: the box is at 7 GB free, under this campaign's 12 GB build floor,
so neither the build nor the suite has been run against this commit. The braces
balance and the reasoning above is stated per test, but that is a review and not
a check.

(cherry picked from commit 481e941)
LAST_COHORT_SPLIT (cfg(test), copying.rs) is test_only; SURVIVOR_ROUND_MEASURED
(tenuring.rs) is a boolean, not a GC pointer. Inventory only; no code change.

(cherry picked from commit 9c85be5)
Snapshot malloc-backed headers before running verifier callbacks so exact
child validation can lazily rebuild the malloc registry without re-entering
its RefCell borrow. Add a worker-thread copying-minor regression fixture.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit 499b716)
Record the re-entrancy path, structural fix, disk-gated validation status,
and the requested perrymaster campaign handoff.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit 4295d39)
Name heap parents, layout slots, root scanners, and collection coverage
when evacuation verification finds a stale forwarding alias. Emit a compact
success witness with heap-walk and remembered-edge counts under GC diagnostics.

Add focused failure-attribution and success-line regression tests.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit 1ec9e0e)
Record VF2 field derivation, covered failure sites, passing-path cost,
focused test evidence, disk-limited gates, and the perrymaster campaign
request.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit 38229cc)
Re-pin the PASS1_MARKED non-moving window after auditing the verifier
diagnostic plumbing, and classify its three counter-only TLS holders.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit 4fc86a2)
Instrument headerless receiver producers and dynamic receiver funnels behind
the opt-in PERRY_RECEIVER_REPR_DIAG sink. Add explicit native return storage
kinds while retaining the existing pointer-boxing lowering, plus checked
provider inventory and fail-capable fixtures.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit 9b6884e)
Record the producer map, corrected executable-row census, sabotage proofs,
gate results, and the perrymaster measurement request for campaign PR 1.

Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
(cherry picked from commit ecbfba3)
#9969 makes run_lint_gates.sh derive every command of every run: step (80
now, up from 67). Two of the newly-run gates were red on this train:

- addr_class_inventory rejected #9973's two GcHeader casts in
  hot_diag/receiver_repr.rs. Both are allowlisted with reasons rather
  than converted: the block is a #[cfg(debug_assertions)] trust-the-tag
  audit that compares the ownership-derived header against a direct
  byte-offset read and counts disagreements. Routing the raw side through
  try_read_gc_header would validate the address first and return None for
  exactly the implausible cases the audit exists to catch, so the
  canonical predicate cannot stand in there.

- RUSTFLAGS="-D warnings" cargo check --workspace --all-targets rejected
  #9861's doc comment on a thread_local! macro invocation, which cannot
  carry one. Moved inside the macro onto the static it describes. This is
  a warning, not an error, so it only fails under -D warnings — which the
  driver never replayed before #9969.
@proggeramlug
proggeramlug merged commit 7806a8e into main Sep 7, 2026
13 of 20 checks passed
@proggeramlug
proggeramlug deleted the train143 branch September 7, 2026 22:06
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 30b56eb3-73f8-4bbb-bc4c-219dffe65387

📥 Commits

Reviewing files that changed from the base of the PR and between 43200e9 and 3f57261.

⛔ Files ignored due to path filters (1)
  • scripts/native_result_ledger.tsv is excluded by !**/*.tsv
📒 Files selected for processing (89)
  • .github/workflows/native-result-ledger.yml
  • cc-perf-campaign/codex/REPORT_receiver_repr_ledger.md
  • cc-perf-campaign/codex/REPORT_verify_evacuation_borrow.md
  • changelog.d/4644-retained-growth-verifier.md
  • changelog.d/9851-lock-rates-one-cohort.md
  • changelog.d/9851-occupancy-may-not-promote-on-first-copy.md
  • changelog.d/9969-lint-gates-extractor-loud.md
  • changelog.d/9970-product-warnings-dead-code.md
  • changelog.d/9971-segview-build-cache-input.md
  • changelog.d/verify-evacuation-malloc-borrow.md
  • crates/perry-codegen/src/lib.rs
  • crates/perry-codegen/src/lower_call/native_module_dispatch.rs
  • crates/perry-codegen/src/lower_call/native_table/async_decimal.rs
  • crates/perry-codegen/src/lower_call/native_table/bun.rs
  • crates/perry-codegen/src/lower_call/native_table/databases.rs
  • crates/perry-codegen/src/lower_call/native_table/extras.rs
  • crates/perry-codegen/src/lower_call/native_table/fastify.rs
  • crates/perry-codegen/src/lower_call/native_table/http_client.rs
  • crates/perry-codegen/src/lower_call/native_table/http_http2.rs
  • crates/perry-codegen/src/lower_call/native_table/http_server.rs
  • crates/perry-codegen/src/lower_call/native_table/media.rs
  • crates/perry-codegen/src/lower_call/native_table/mod.rs
  • crates/perry-codegen/src/lower_call/native_table/net_classes_state.rs
  • crates/perry-codegen/src/lower_call/native_table/net_events.rs
  • crates/perry-codegen/src/lower_call/native_table/node_core/dgram_fs_os.rs
  • crates/perry-codegen/src/lower_call/native_table/node_core/util_buffer.rs
  • crates/perry-codegen/src/lower_call/native_table/node_core_process.rs
  • crates/perry-codegen/src/lower_call/native_table/node_domain.rs
  • crates/perry-codegen/src/lower_call/native_table/node_misc.rs
  • crates/perry-codegen/src/lower_call/native_table/thread_lodash.rs
  • crates/perry-codegen/src/lower_call/native_table/tls_events.rs
  • crates/perry-codegen/src/lower_call/native_table/tui.rs
  • crates/perry-codegen/src/lower_call/native_table/undici.rs
  • crates/perry-codegen/src/lower_call/native_table/utils_crypto.rs
  • crates/perry-codegen/src/lower_call/native_table/ws_events.rs
  • crates/perry-runtime/src/async_hooks.rs
  • crates/perry-runtime/src/buffer/header.rs
  • crates/perry-runtime/src/buffer/mod.rs
  • crates/perry-runtime/src/gc/barrier_store.rs
  • crates/perry-runtime/src/gc/copying.rs
  • crates/perry-runtime/src/gc/cycle.rs
  • crates/perry-runtime/src/gc/instruments.rs
  • crates/perry-runtime/src/gc/mod.rs
  • crates/perry-runtime/src/gc/roots.rs
  • crates/perry-runtime/src/gc/telemetry.rs
  • crates/perry-runtime/src/gc/tenuring.rs
  • crates/perry-runtime/src/gc/tests/copying.rs
  • crates/perry-runtime/src/gc/tests/copying/adaptive_tenuring.rs
  • crates/perry-runtime/src/gc/tests/copying/promoted_remembered_7803.rs
  • crates/perry-runtime/src/gc/tests/copying/survival_and_malloc.rs
  • crates/perry-runtime/src/gc/tests/copying/verify_malloc_borrow.rs
  • crates/perry-runtime/src/gc/tests/copying/verify_parent_context.rs
  • crates/perry-runtime/src/gc/tests/copying/weak_holder_registry.rs
  • crates/perry-runtime/src/gc/tests/oldgen.rs
  • crates/perry-runtime/src/gc/tests/runtime_roots/hook_dispatch_handles.rs
  • crates/perry-runtime/src/gc/tests/support.rs
  • crates/perry-runtime/src/gc/verify.rs
  • crates/perry-runtime/src/gc/verify_diag.rs
  • crates/perry-runtime/src/hot_diag.rs
  • crates/perry-runtime/src/hot_diag/receiver_repr.rs
  • crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rs
  • crates/perry-runtime/src/object/field_get_set/ic_miss.rs
  • crates/perry-runtime/src/object/mod.rs
  • crates/perry-runtime/src/object/native_call_method.rs
  • crates/perry-runtime/src/object/native_call_method/primitive_methods.rs
  • crates/perry-runtime/src/object/null_stub.rs
  • crates/perry-runtime/src/object/prototype_chain.rs
  • crates/perry-runtime/src/object/regex_proto_thunks.rs
  • crates/perry-runtime/src/proxy.rs
  • crates/perry-runtime/src/shared_sab.rs
  • crates/perry-runtime/src/symbol.rs
  • crates/perry-runtime/src/symbol/constructors.rs
  • crates/perry-runtime/src/text.rs
  • crates/perry-runtime/src/timer.rs
  • crates/perry-runtime/src/tui/hooks.rs
  • crates/perry-runtime/src/tui/mod.rs
  • crates/perry-runtime/src/tui/state.rs
  • crates/perry-runtime/src/tui/tree.rs
  • crates/perry-runtime/src/value/addr_class.rs
  • crates/perry-stdlib/src/common/handle.rs
  • crates/perry-stdlib/src/fetch/mod.rs
  • crates/perry-stdlib/src/zlib.rs
  • crates/perry/src/commands/compile/build_cache.rs
  • crates/perry/tests/native_link_cache.rs
  • scripts/addr_class_allowlist.txt
  • scripts/gc_instrument_smoke.sh
  • scripts/gc_runtime_root_holders.json
  • scripts/native_result_ledger.py
  • scripts/run_lint_gates.sh

📝 Walkthrough

Walkthrough

This PR adds typed native result classes and validation, receiver-representation diagnostics, GC evacuation-verifier and adaptive-tenuring updates, and build or lint tooling changes. It also updates tests, reports, smoke checks, and changelog entries to match the new behavior.

Changes

Typed native results and ledger validation

Layer / File(s) Summary
Return kind contracts
crates/perry-codegen/src/lower_call/native_table/mod.rs, crates/perry-codegen/src/lower_call/native_module_dispatch.rs, crates/perry-codegen/src/lib.rs
NativeRetKind::Ptr is replaced by five typed result kinds. Lowering still routes these kinds through the existing native-handle path, and docs now list the new tags.
Native table reclassification
crates/perry-codegen/src/lower_call/native_table/*, crates/perry-codegen/src/lower_call/native_table/node_core/*
Native-table rows across async hooks, databases, HTTP, media, net, TLS, TUI, crypto, and other modules now replace NR_PTR with NR_GCPTR, NR_NULLABLE_GCPTR, NR_HANDLE_ID, NR_FOREIGN_PTR, or NR_JS_VALUE.
Ledger script and workflow
scripts/native_result_ledger.py, .github/workflows/native-result-ledger.yml
A new validator scans typed native rows, rejects legacy NR_PTR, checks provider metadata, and exposes a self-test. A new workflow runs the self-test and the full validator on matching changes.
Ledger documentation
cc-perf-campaign/codex/REPORT_receiver_repr_ledger.md
The report records the typed native-result census, provider coverage, validator behavior, tests, workflow checks, and follow-up measurement instructions.

Receiver representation diagnostics

Layer / File(s) Summary
Diagnostic core and classification
crates/perry-runtime/src/hot_diag.rs, crates/perry-runtime/src/hot_diag/receiver_repr.rs, crates/perry-runtime/src/value/addr_class.rs, crates/perry-runtime/src/buffer/*, crates/perry-runtime/src/async_hooks.rs, crates/perry-runtime/src/object/*, crates/perry-runtime/src/tui/*
The runtime adds a receiver_repr diagnostic module, exposes sink helpers to the crate, adds receiver families and counters, classifies observed values and decoded pointers, and adds lookup helpers for several handle registries and singleton addresses.
Producer and funnel instrumentation
crates/perry-runtime/src/async_hooks.rs, crates/perry-runtime/src/buffer/header.rs, crates/perry-runtime/src/proxy.rs, crates/perry-runtime/src/shared_sab.rs, crates/perry-runtime/src/symbol*.rs, crates/perry-runtime/src/text.rs, crates/perry-runtime/src/timer.rs, crates/perry-runtime/src/tui/*, crates/perry-stdlib/src/common/handle.rs, crates/perry-stdlib/src/fetch/mod.rs, crates/perry-stdlib/src/zlib.rs, crates/perry-runtime/src/object/field_get_set/*, crates/perry-runtime/src/object/native_call_method*.rs, crates/perry-runtime/src/object/prototype_chain.rs
Producer sites now record constructed receiver families, and receiver access funnels now record values or decoded pointers when receiver_repr_on() is active.
Tests and audit support
crates/perry-runtime/src/hot_diag/receiver_repr.rs, crates/perry-runtime/src/object/native_call_method.rs, scripts/addr_class_allowlist.txt
The new module adds fixture and source-witness tests. Native-call tests verify that the unarmed path does not enter classification. The allowlist records debug-only header casts used by the diagnostic audit.

GC verifier and adaptive tenuring

Layer / File(s) Summary
Verifier context and diagnostics
crates/perry-runtime/src/gc/verify.rs, crates/perry-runtime/src/gc/verify_diag.rs, crates/perry-runtime/src/gc/cycle.rs, crates/perry-runtime/src/gc/roots.rs, crates/perry-runtime/src/gc/instruments.rs, crates/perry-runtime/src/gc/mod.rs
Evacuation verification now snapshots malloc headers before validation, carries cycle and parent context, returns verification stats, labels root scans, and emits detailed failure or pass diagnostics with per-thread counters.
Fresh-cohort accounting and policy
crates/perry-runtime/src/gc/copying.rs, crates/perry-runtime/src/gc/telemetry.rs, crates/perry-runtime/src/gc/tenuring.rs
Minor-copying accounting now separates whole-survivor values from fresh-cohort values. Telemetry exports the new counters. Adaptive tenuring now starts at an occupancy floor of 2, gates startup decisions on measured rounds, and rates the survival lock from fresh-cohort data.
Tests, smoke checks, and notes
crates/perry-runtime/src/gc/tests/*, scripts/gc_instrument_smoke.sh, scripts/gc_runtime_root_holders.json, cc-perf-campaign/codex/REPORT_verify_evacuation_borrow.md, changelog.d/4644-retained-growth-verifier.md, changelog.d/9851-*, changelog.d/verify-evacuation-malloc-borrow.md
GC tests now cover malloc-borrow release, parent-context diagnostics, pass-line output, fresh-cohort accounting, and the updated tenuring policy. Smoke checks, holder metadata, reports, and changelog notes describe the same behavior changes.

Tooling and build behavior

Layer / File(s) Summary
Lint gate extraction rewrite
scripts/run_lint_gates.sh, changelog.d/9969-lint-gates-extractor-loud.md
run_lint_gates.sh now has a self-test mode, uses a Python extractor for workflow commands, enforces non-empty run: steps, keeps explicit CI-only skips, derives compile commands from workflow jobs, and prints step-based list and run output.
Build cache and product-build fixes
crates/perry/src/commands/compile/build_cache.rs, crates/perry/tests/native_link_cache.rs, crates/perry-runtime/src/gc/barrier_store.rs, crates/perry-runtime/src/object/regex_proto_thunks.rs, changelog.d/9970-product-warnings-dead-code.md, changelog.d/9971-segview-build-cache-input.md
The build cache now keys on PERRY_SEGVIEW and excludes PERRY_SEGVIEW_DIAG. A new test checks cache misses and hits across PERRY_SEGVIEW changes. Regex-only barrier and prototype-test state are now compiled only for tests or the regex-engine feature.

Estimated code review effort: 5 (Critical) | ~120 minutes

Suggested reviewers: jdalton

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch train143

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant